ci(docker): allow a manual rebuild via workflow_dispatch - #129
Merged
Merged
Conversation
Docker only ran on a push to main or a version tag. main is protected by required status checks, so producing an image -- and reaching notify-ops and the deploy behind it -- always meant opening a pull request, even when nothing about the code needed to change. That gap cost three weeks in September 2026: the credential notify-ops uses had expired, it failed with HTTP 401 on every run, and no deploy reached production. Once the credential was replaced there was no way to prove it worked, or to redeploy the current main, without inventing a code change to push. An empty commit is rejected by the branch rules, which is correct and not something to work around. workflow_dispatch rebuilds from whatever main currently holds and runs the whole chain: build, push, sign, notify, deploy. That is also what is wanted during an incident, where the question is usually "redeploy what is on main" rather than "ship a change". Nothing else moves. The build, the matrix, the signing and the permissions are untouched, and the repo's supply-chain guards still pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MrChengLen
force-pushed
the
ci/docker-manual-trigger
branch
from
September 14, 2026 17:20
e6f0d46 to
f72dedc
Compare
MrChengLen
added a commit
that referenced
this pull request
Sep 25, 2026
…ships The image installs requirements.lock with --require-hashes, but four workflows still installed requirements.txt, whose >= ranges resolve to the newest PyPI releases. So CI tested, and the SBOM described, versions the image does not ship. lint-and-test: installs requirements-dev.txt with the lockfile's pins as constraints. SQLAlchemy 2.1.0 reached CI unpinned and failed a test on every branch while the image stayed on 2.0.52. The hashes are stripped into $RUNNER_TEMP/constraints.txt because pip switches the whole install to --require-hashes as soon as one constraint carries a hash (checked with pip 26.2.1), and the dev tools are unhashed. The step fails if any lockfile entry does not become a constraint, instead of letting it install unpinned. The dev-only tools are not in the lockfile: a pip dry-run for cp314/manylinux resolves them to the same pytest, ruff, uv, aiosqlite, httpx, pip-audit, pyinstaller and bs4 versions as without constraints, and 76 of the 77 locked packages at their locked version (uvloop, Linux-only, was skipped on the Windows host that ran it). A floor raised past the locked version (the usual Dependabot pip PR) now fails this install as well as lockfile-drift; recompiling fixes both. A dev tool that needs a newer locked package fails here with lockfile-drift green; the ci.yml comment gives the --upgrade-package recompile for that case. deps-latest (new): the unpinned install plus pytest, weekly on Mondays and on demand, gating nothing. It keeps the early warning that caught 2.1 before any lockfile bump. Not a continue-on-error job in ci.yml: that still shows a red check on every PR whenever upstream breaks. sbom / release: the SBOM described the runner's own Python after `pip install -r requirements.txt` plus cyclonedx-bom. main's SBOM at 1d7bad6 had 106 components against 77 in the lockfile: 19 at versions the image does not contain, 28 from the generator itself, and packaging downgraded from 26.3 to 25.0 by the generator's install. Now: a fresh venv installed the Dockerfile's way, the generator outside it, and `cyclonedx-py environment <venv python>` (checked with cyclonedx-bom 5.5.0). Rejected `cyclonedx-py requirements requirements.lock`: it parses the hashed lockfile fine but emits no licences and no dependency graph (main's SBOM carries 70 licensed components, 61 graph entries). The docs now say which workflow builds the release SBOM (release.yml, not sbom.yml) and that system packages are not in it. verapdf: built its PDF/A fixture with the newest pikepdf; it now installs the lockfile the image's way (all 77 locked versions have cp314 manylinux wheels). build-desktop.yml still installs requirements.txt: it builds on Windows, the lockfile is Linux-only. docker.yml: renormalized to LF. PR #129 committed it with CRLF via an API commit despite `*.yml text eol=lf`, so every checkout showed it as modified. No content change. tests/test_supply_chain_hygiene.py pins all of it, including that deps-latest keeps lint-and-test's system packages and pytest call; each of 10 simulated reverts fails a guard. Local suite 1225 passed, 63 skipped (native-library tests run in CI); ruff clean; python-version gate green; i18n and pip-audit inputs untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MrChengLen
added a commit
that referenced
this pull request
Sep 25, 2026
…ships The image installs requirements.lock with --require-hashes, but four workflows still installed requirements.txt, whose >= ranges resolve to the newest PyPI releases. So CI tested, and the SBOM described, versions the image does not ship. lint-and-test: installs requirements-dev.txt with the lockfile's pins as constraints. SQLAlchemy 2.1.0 reached CI unpinned and failed a test on every branch while the image stayed on 2.0.52. The hashes are stripped into $RUNNER_TEMP/constraints.txt because pip switches the whole install to --require-hashes as soon as one constraint carries a hash (checked with pip 26.2.1), and the dev tools are unhashed. The step fails if any lockfile entry does not become a constraint, instead of letting it install unpinned. The dev-only tools are not in the lockfile: a pip dry-run for cp314/manylinux resolves them to the same pytest, ruff, uv, aiosqlite, httpx, pip-audit, pyinstaller and bs4 versions as without constraints, and 76 of the 77 locked packages at their locked version (uvloop, Linux-only, was skipped on the Windows host that ran it). A floor raised past the locked version (the usual Dependabot pip PR) now fails this install as well as lockfile-drift; recompiling fixes both. A dev tool that needs a newer locked package fails here with lockfile-drift green; the ci.yml comment gives the --upgrade-package recompile for that case. deps-latest (new): the unpinned install plus pytest, weekly on Mondays and on demand, gating nothing. It keeps the early warning that caught 2.1 before any lockfile bump. Not a continue-on-error job in ci.yml: that still shows a red check on every PR whenever upstream breaks. sbom / release: the SBOM described the runner's own Python after `pip install -r requirements.txt` plus cyclonedx-bom. main's SBOM at 1d7bad6 had 106 components against 77 in the lockfile: 19 at versions the image does not contain, 28 from the generator itself, and packaging downgraded from 26.3 to 25.0 by the generator's install. Now: a fresh venv installed the Dockerfile's way, the generator outside it, and `cyclonedx-py environment <venv python>` (checked with cyclonedx-bom 5.5.0). Rejected `cyclonedx-py requirements requirements.lock`: it parses the hashed lockfile fine but emits no licences and no dependency graph (main's SBOM carries 70 licensed components, 61 graph entries). The docs now say which workflow builds the release SBOM (release.yml, not sbom.yml) and that system packages are not in it. verapdf: built its PDF/A fixture with the newest pikepdf; it now installs the lockfile the image's way (all 77 locked versions have cp314 manylinux wheels). build-desktop.yml still installs requirements.txt: it builds on Windows, the lockfile is Linux-only. docker.yml: renormalized to LF. PR #129 committed it with CRLF via an API commit despite `*.yml text eol=lf`, so every checkout showed it as modified. No content change. tests/test_supply_chain_hygiene.py pins all of it, including that deps-latest keeps lint-and-test's system packages and pytest call; each of 10 simulated reverts fails a guard. Local suite 1239 passed, 63 skipped (native-library tests run in CI); ruff clean; python-version gate green; i18n and pip-audit inputs untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warum
Dockerlief bisher nur bei einem Push aufmainoder einem Versions-Tag.mainist durch Pflicht-Checks geschützt — ein Image zu bauen (und damitnotify-opsund den Deploy dahinter zu erreichen) setzte also immer einen Pull Request voraus, selbst wenn am Code nichts zu ändern war.Genau diese Lücke war letzte Woche das Problem: Die Zugangsdaten, die
notify-opsverwendet, waren am 20.08. abgelaufen; der Workflow scheiterte danach bei jedem Lauf mit HTTP 401, und drei Wochen lang erreichte kein Deploy die Produktion. Nach dem Ersetzen des Tokens gab es keine Möglichkeit, die neue Berechtigung zu prüfen oder den aktuellenmain-Stand neu auszurollen, ohne eine Code-Änderung zu erfinden. Ein leerer Commit wird von den Branch-Regeln zurückgewiesen — korrekt so, und nichts, was man umgehen sollte.workflow_dispatchbaut aus dem, was gerade aufmainliegt, und durchläuft die ganze Kette: bauen, pushen, signieren, benachrichtigen, deployen. Das ist auch das, was man im Störungsfall braucht, wo die Frage meist „den Stand von main neu ausrollen" lautet und nicht „eine Änderung ausliefern".Was sich sonst ändert
Nichts. Build, Matrix, Signierung und Permissions bleiben unangetastet; die Supply-Chain-Guards des Repos greifen weiterhin (SHA-gepinnte Actions, expliziter
permissions-Block) —tests/test_supply_chain_hygiene.pyläuft grün durch.Nach dem Merge
Der Merge selbst ist zugleich der erste Test des neuen Tokens: Er pusht auf
main, also bautdocker.ymlundnotify-ops.ymlfeuert. Ist der Lauf grün, ist die Kette nachweislich wieder intakt. Danach steht der Knopf unter Actions → Docker → Run workflow dauerhaft zur Verfügung.🤖 Generated with Claude Code
Nachtrag: Die erste Fassung nannte das Secret beim Namen und wurde vom
scope-checkblockiert — korrekt, der Name gehört ausschließlich innotify-ops.yml, die dafür auf der Allowlist steht. Der Kommentar beschreibt den Vorfall jetzt ohne ihn.